﻿function isNumeric ( obj ) {
    return !isNaN( parseFloat(obj) ) && isFinite( obj );
}

// jQuery Alert Dialogs Plugin
//
// Version 1.1
//
// Cory S.N. LaViska
// A Beautiful Site (http://abeautifulsite.net/)
// 14 May 2009
//
// Visit http://abeautifulsite.net/notebook/87 for more information
//
// Usage:
//		jAlert( message, [title, callback] )
//		jConfirm( message, [title, callback] )
//		jPrompt( message, [value, title, callback] )
// 
// History:
//
//		1.00 - Released (29 December 2008)
//
//		1.01 - Fixed bug where unbinding would destroy all resize events
//
// License:
// 
// This plugin is dual-licensed under the GNU General Public License and the MIT License and
// is copyright 2008 A Beautiful Site, LLC. 
//
(function($) {
	
	$.alerts = {
		
		// These properties can be read/written by accessing $.alerts.propertyName from your scripts at any time
		
		verticalOffset: -75,                // vertical offset of the dialog from center screen, in pixels
		horizontalOffset: 0,                // horizontal offset of the dialog from center screen, in pixels/
		repositionOnResize: true,           // re-centers the dialog on window resize
		overlayOpacity: .01,                // transparency level of overlay
		overlayColor: '#FFF',               // base color of overlay
		draggable: true,                    // make the dialogs draggable (requires UI Draggables plugin)
		okButton: '&nbsp;OK&nbsp;',         // text for the OK button
		cancelButton: '&nbsp;Cancel&nbsp;', // text for the Cancel button
		dialogClass: null,                  // if specified, this class will be applied to all dialogs
		
		// Public methods
		
		alert: function(message, title, callback) {
			if( title == null ) title = 'Alert';
			$.alerts._show(title, message, null, 'alert', function(result) {
				if( callback ) callback(result);
			});
		},
		
		confirm: function(message, title, callback) {
			if( title == null ) title = 'Confirm';
			$.alerts._show(title, message, null, 'confirm', function(result) {
				if( callback ) callback(result);
			});
		},
			
		prompt: function(message, value, title, callback) {
			if( title == null ) title = 'Prompt';
			$.alerts._show(title, message, value, 'prompt', function(result) {
				if( callback ) callback(result);
			});
		},
		
		// Private methods
		
		_show: function(title, msg, value, type, callback) {
			
			$.alerts._hide();
			$.alerts._overlay('show');
			
			$("BODY").append(
			  '<div id="popup_container">' +
			    '<h1 id="popup_title"></h1>' +
			    '<div id="popup_content">' +
			      '<div id="popup_message"></div>' +
				'</div>' +
			  '</div>');
			
			if( $.alerts.dialogClass ) $("#popup_container").addClass($.alerts.dialogClass);
			
			// IE6 Fix
			var pos = ($.browser.msie && parseInt($.browser.version) <= 6 ) ? 'absolute' : 'fixed'; 
			
			$("#popup_container").css({
				position: pos,
				zIndex: 99999,
				padding: 0,
				margin: 0
			});
			
			$("#popup_title").text(title);
			$("#popup_content").addClass(type);
			$("#popup_message").text(msg);
			$("#popup_message").html( $("#popup_message").text().replace(/\n/g, '<br />') );
			
			$("#popup_container").css({
				minWidth: $("#popup_container").outerWidth(),
				maxWidth: $("#popup_container").outerWidth()
			});
			
			$.alerts._reposition();
			$.alerts._maintainPosition(true);
			
			switch( type ) {
				case 'alert':
					$("#popup_message").after('<div id="popup_panel"><input type="button" value="' + $.alerts.okButton + '" id="popup_ok" /></div>');
					$("#popup_ok").click( function() {
						$.alerts._hide();
						callback(true);
					});
					$("#popup_ok").focus().keypress( function(e) {
						if( e.keyCode == 13 || e.keyCode == 27 ) $("#popup_ok").trigger('click');
					});
				break;
				case 'confirm':
					$("#popup_message").after('<div id="popup_panel"><input type="button" value="' + $.alerts.okButton + '" id="popup_ok" /> <input type="button" value="' + $.alerts.cancelButton + '" id="popup_cancel" /></div>');
					$("#popup_ok").click( function() {
						$.alerts._hide();
						if( callback ) callback(true);
					});
					$("#popup_cancel").click( function() {
						$.alerts._hide();
						if( callback ) callback(false);
					});
					$("#popup_ok").focus();
					$("#popup_ok, #popup_cancel").keypress( function(e) {
						if( e.keyCode == 13 ) $("#popup_ok").trigger('click');
						if( e.keyCode == 27 ) $("#popup_cancel").trigger('click');
					});
				break;
				case 'prompt':
					$("#popup_message").append('<br /><input type="text" size="30" id="popup_prompt" />').after('<div id="popup_panel"><input type="button" value="' + $.alerts.okButton + '" id="popup_ok" /> <input type="button" value="' + $.alerts.cancelButton + '" id="popup_cancel" /></div>');
					$("#popup_prompt").width( $("#popup_message").width() );
					$("#popup_ok").click( function() {
						var val = $("#popup_prompt").val();
						$.alerts._hide();
						if( callback ) callback( val );
					});
					$("#popup_cancel").click( function() {
						$.alerts._hide();
						if( callback ) callback( null );
					});
					$("#popup_prompt, #popup_ok, #popup_cancel").keypress( function(e) {
						if( e.keyCode == 13 ) $("#popup_ok").trigger('click');
						if( e.keyCode == 27 ) $("#popup_cancel").trigger('click');
					});
					if( value ) $("#popup_prompt").val(value);
					$("#popup_prompt").focus().select();
				break;
			}
			
			// Make draggable
			if( $.alerts.draggable ) {
				try {
					$("#popup_container").draggable({ handle: $("#popup_title") });
					$("#popup_title").css({ cursor: 'move' });
				} catch(e) { /* requires jQuery UI draggables */ }
			}
		},
		
		_hide: function() {
			$("#popup_container").remove();
			$.alerts._overlay('hide');
			$.alerts._maintainPosition(false);
		},
		
		_overlay: function(status) {
			switch( status ) {
				case 'show':
					$.alerts._overlay('hide');
					$("BODY").append('<div id="popup_overlay"></div>');
					$("#popup_overlay").css({
						position: 'absolute',
						zIndex: 99998,
						top: '0px',
						left: '0px',
						width: '100%',
						height: $(document).height(),
						background: $.alerts.overlayColor,
						opacity: $.alerts.overlayOpacity
					});
				break;
				case 'hide':
					$("#popup_overlay").remove();
				break;
			}
		},
		
		_reposition: function() {
			var top = (($(window).height() / 2) - ($("#popup_container").outerHeight() / 2)) + $.alerts.verticalOffset;
			var left = (($(window).width() / 2) - ($("#popup_container").outerWidth() / 2)) + $.alerts.horizontalOffset;
			if( top < 0 ) top = 0;
			if( left < 0 ) left = 0;
			
			// IE6 fix
			if( $.browser.msie && parseInt($.browser.version) <= 6 ) top = top + $(window).scrollTop();
			
			$("#popup_container").css({
				top: top + 'px',
				left: left + 'px'
			});
			$("#popup_overlay").height( $(document).height() );
		},
		
		_maintainPosition: function(status) {
			if( $.alerts.repositionOnResize ) {
				switch(status) {
					case true:
						$(window).bind('resize', $.alerts._reposition);
					break;
					case false:
						$(window).unbind('resize', $.alerts._reposition);
					break;
				}
			}
		}
		
	}
	
	// Shortuct functions
	jAlert = function(message, title, callback) {
		$.alerts.alert(message, title, callback);
	}
	
	jConfirm = function(message, title, callback) {
		$.alerts.confirm(message, title, callback);
	};
		
	jPrompt = function(message, value, title, callback) {
		$.alerts.prompt(message, value, title, callback);
	};
	
})(jQuery);



// jQuery Context Menu Plugin
//
// Version 1.01
//
// Cory S.N. LaViska
// A Beautiful Site (http://abeautifulsite.net/)
//
// More info: http://abeautifulsite.net/2008/09/jquery-context-menu-plugin/
//
// Terms of Use
//
// This plugin is dual-licensed under the GNU General Public License
//   and the MIT License and is copyright A Beautiful Site, LLC.
//
if (jQuery) (function () {
    $.extend($.fn, {

        contextMenu: function (o, callback) {
            // Defaults

            var $menu = $('.' + o.menu, $(this).parent(".tabularData"));


            if (o.menu == undefined) return false;
            if (o.inSpeed == undefined) o.inSpeed = 150;
            if (o.outSpeed == undefined) o.outSpeed = 75;
            // 0 needs to be -1 for expected results (no fade)
            if (o.inSpeed == 0) o.inSpeed = -1;
            if (o.outSpeed == 0) o.outSpeed = -1;
            // Loop each context menu
            $(this).each(function () {
                var el = $(this);
                var offset = $(el).offset();
                // Add contextMenu class
                $menu.addClass('contextMenu');
                // Simulate a true right click
                $(this).mousedown(function (e) {



                    var evt = e;
                    evt.stopPropagation();
                    $(this).mouseup(function (e) {
                        e.stopPropagation();
                        var srcElement = $(this);
                        var actElement = $(e.target); // <---- get actual item clicked

                        $(this).unbind('mouseup');
                        if (evt.button == 2) {
                            // Hide context menus that may be showing
                            $(".contextMenu").hide();
                            // Get this context menu
                            var menu = $menu; // <--- changed so that it looks within the current datatype

                            if ($(el).hasClass('disabled')) return false;

                            // Detect mouse position
                            var d = {}, x, y;
                            if (self.innerHeight) {
                                d.pageYOffset = self.pageYOffset;
                                d.pageXOffset = self.pageXOffset;
                                d.innerHeight = self.innerHeight;
                                d.innerWidth = self.innerWidth;
                            } else if (document.documentElement &&
								document.documentElement.clientHeight) {
                                d.pageYOffset = document.documentElement.scrollTop;
                                d.pageXOffset = document.documentElement.scrollLeft;
                                d.innerHeight = document.documentElement.clientHeight;
                                d.innerWidth = document.documentElement.clientWidth;
                            } else if (document.body) {
                                d.pageYOffset = document.body.scrollTop;
                                d.pageXOffset = document.body.scrollLeft;
                                d.innerHeight = document.body.clientHeight;
                                d.innerWidth = document.body.clientWidth;
                            }
                            (e.pageX) ? x = e.pageX : x = e.clientX + d.scrollLeft;
                            (e.pageY) ? y = e.pageY : y = e.clientY + d.scrollTop;

                            //alert('(' + x + ',' + y + ')');

                            var $container = $(this).parent(".tabularData").offset();
                            //alert('container = (' + $container.top + ',' + $container.left + ')');


                            //alert(e.pageX +', '+ e.pageY);

                            // Show the menu
                            $(document).unbind('click');
                            $(menu).css({ top: y - $container.top, left: x }).fadeIn(o.inSpeed);
                            // Hover events
                            $(menu).find('a').mouseover(function () {
                                $(menu).find('li.hover').removeClass('hover');
                                $(this).parent().addClass('hover');
                            }).mouseout(function () {
                                $(menu).find('li.hover').removeClass('hover');
                            });

                            // Keyboard
                            $(document).keypress(function (e) {
                                switch (e.keyCode) {
                                    case 38: // up
                                        if ($(menu).find('li.hover').size() == 0) {
                                            $(menu).find('li:last').addClass('hover');
                                        } else {
                                            $(menu).find('li.hover').removeClass('hover').prevAll('li:not(.disabled)').eq(0).addClass('hover');
                                            if ($(menu).find('li.hover').size() == 0) $(menu).find('li:last').addClass('hover');
                                        }
                                        break;
                                    case 40: // down
                                        if ($(menu).find('li.hover').size() == 0) {
                                            $(menu).find('li:first').addClass('hover');
                                        } else {
                                            $(menu).find('li.hover').removeClass('hover').nextAll('li:not(.disabled)').eq(0).addClass('hover');
                                            if ($(menu).find('li.hover').size() == 0) $(menu).find('li:first').addClass('hover');
                                        }
                                        break;
                                    case 13: // enter
                                        $(menu).find('li.hover a').trigger('click');
                                        break;
                                    case 27: // esc
                                        $(document).trigger('click');
                                        break
                                }
                            });

                            // When items are selected
                            $menu.find('a').unbind('click');
                            $menu.find('li:not(.disabled) a').click(function () {
                                $(document).unbind('click').unbind('keypress');
                                $(".contextMenu").hide();
                                // Callback
                                if (callback) callback($(this).attr('href').substr(1), $(srcElement), $(actElement), { x: x - offset.left, y: y - offset.top, docX: x, docY: y });
                                return false;
                            });

                            // Hide bindings
                            setTimeout(function () { // Delay for Mozilla
                                $(document).click(function () {
                                    $(document).unbind('click').unbind('keypress');
                                    $(menu).fadeOut(o.outSpeed);
                                    return false;
                                });
                            }, 0);
                        }
                    });
                });

                // Disable text selection
                if ($.browser.mozilla) {
                    $menu.each(function () { $(this).css({ 'MozUserSelect': 'none' }); });
                } else if ($.browser.msie) {
                    $menu.each(function () { $(this).bind('selectstart.disableTextSelect', function () { return false; }); });
                } else {
                    $menu.each(function () { $(this).bind('mousedown.disableTextSelect', function () { return false; }); });
                }
                // Disable browser context menu (requires both selectors to work in IE/Safari + FF/Chrome)
                $(el).add($('ul.contextMenu')).bind('contextmenu', function () { return false; });

            });
            return $(this);
        },

        // Disable context menu items on the fly
        disableContextMenuItems: function (o) {
            if (o == undefined) {
                // Disable all
                $(this).find('li').addClass('disabled');
                return ($(this));
            }
            $(this).each(function () {
                if (o != undefined) {
                    var d = o.split(',');
                    for (var i = 0; i < d.length; i++) {
                        $(this).find('a[href="' + d[i] + '"]').parent().addClass('disabled');

                    }
                }
            });
            return ($(this));
        },

        // Enable context menu items on the fly
        enableContextMenuItems: function (o) {
            if (o == undefined) {
                // Enable all
                $(this).find('li.disabled').removeClass('disabled');
                return ($(this));
            }
            $(this).each(function () {
                if (o != undefined) {
                    var d = o.split(',');
                    for (var i = 0; i < d.length; i++) {
                        $(this).find('a[href="' + d[i] + '"]').parent().removeClass('disabled');

                    }
                }
            });
            return ($(this));
        },

        // Disable context menu(s)
        disableContextMenu: function () {
            $(this).each(function () {
                $(this).addClass('disabled');
            });
            return ($(this));
        },

        // Enable context menu(s)
        enableContextMenu: function () {
            $(this).each(function () {
                $(this).removeClass('disabled');
            });
            return ($(this));
        },

        // Destroy context menu(s)
        destroyContextMenu: function () {
            // Destroy specified context menus
            $(this).each(function () {
                // Disable action
                $(this).unbind('mousedown').unbind('mouseup');
            });
            return ($(this));
        }

    });
})(jQuery);



/**
* jQuery xml plugin
* Converts XML node(s) to string 
*
* Copyright (c) 2009 Radim Svoboda
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* @author 	Radim Svoboda, user Zzzzzz
* @version 1.0.0
*/


/**
* Converts XML node(s) to string using web-browser features.
* Similar to .html() with HTML nodes 
* This method is READ-ONLY.
*  
* @param all set to TRUE (1,"all",etc.) process all elements,
* otherwise process content of the first matched element 
*  
* @return string obtained from XML node(s)  
*/
jQuery.fn.xml = function (all) {

    //result to return
    var s = "";

    //Anything to process ?
    if (this.length)

    //"object" with nodes to convert to string  
        (
      ((typeof all != 'undefined') && all) ?
    //all the nodes 
      this
      :
    //content of the first matched element 
      jQuery(this[0]).contents()
    )
    //convert node(s) to string  
   .each(function () {
       s += window.ActiveXObject ? //==  IE browser ?
       //for IE
	     this.xml
	     :
       //for other browsers
	     (new XMLSerializer()).serializeToString(this)
	     ;
   });


    return s;

};





(function($){
 

     // Cross-browser xml parsing
    // (xml & tmp used internally)
    // replicated from jQuery core for compatibility with earlier jQuery versions
    function parseXML ( data , xml , tmp ) {

        if ( window.DOMParser ) { // Standard
        tmp = new DOMParser();
        xml = tmp.parseFromString( data , "text/xml" );
        } else { // IE
        xml = new ActiveXObject( "Microsoft.XMLDOM" );
        xml.async = "false";
        xml.loadXML( data );
        }

        tmp = xml.documentElement;

        if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) {
        jQuery.error( "Invalid XML: " + data );
        }

        return xml;
    }


    $.fn.extend({ 
         
        tabularData: function() {

            
            // parse table element passed in parameter and return XML string
            function getXmlFromTable ($t) {                
                var $h = $t.children("thead").eq(0);
                var $b = $t.children("tbody").eq(0);
                var $f = $t.children("tfoot").eq(0);

                // create array of column widths
                var columnWidths = new Array();
                if($b[0]) {
                    $b.children("tr").eq(0).children().each(function(){
                        columnWidthStyle = $(this).children("input").eq(0).width();
                        columnWidths.push(columnWidthStyle);
                    });
                }

                var hasHeaderCol = ($b[0] && $b.children("tr").children().eq(0).get(0).tagName == "TH");
                
                var x = "<Data>";

                if(columnWidths.length > 0)
                {
                    x+="<Columns>";
                    for(var i=0; i<columnWidths.length; i++) {
                        x+="<Column width=\"" + columnWidths[i] + "\" />";
                    }
                    x+="</Columns>";
                }

                if($h[0]) {
                    x+="<Header><Row>";
                    $h.children("tr").eq(0).children().each(function(){
                        var xs = $(this).children("input").eq(0).val();
                        x+=(xs=="")?"<ColHead />":"<ColHead><![CDATA["+xs+"]]></ColHead>";
                    });
                    x+="</Row></Header>";
                }
                if($b[0]) {
                    x+="<Body>";
                    $b.children("tr").each(function(){
                        x+="<Row>";
                        $(this).children().each(function(){
                            var xs = $(this).children("input").eq(0).val();
                            x+=($(this).get(0).tagName == "TH")?"<RowHead>":"<Cell>";
                            if(xs!=""){x+="<![CDATA["+xs+"]]>";}
                            x+=($(this).get(0).tagName == "TH")?"</RowHead>":"</Cell>";
                        });
                        x+="</Row>";
                    });
                    x+="</Body>";
                }
                if($f[0]) {
                    x+="<Footer><Row>";
                    $f.children("tr").eq(0).children().each(function(){
                        var xs = $(this).children("input").eq(0).val();
                        x+=($(this).get(0).tagName == "TH")?"<RowHead>":"<Cell>";
                        if(xs!=""){x+="<![CDATA["+xs+"]]>";}
                        x+=($(this).get(0).tagName == "TH")?"</RowHead>":"</Cell>";
                    });
                    x+="</Row></Footer>";
                }
                x+="</Data>";
                //alert(x);
                return(x);
            }


            
            function deleteColumn (tbl, colIdx, doQuietly)
            {
                // deletes column at index col
                var hasHeaderCol = (tbl.find("tbody tr").eq(0).children(0).get(0).tagName == "TH");
                var headerAdjust = (hasHeaderCol ? 2 : 1);
                if(doQuietly)
                    $('tr td:nth-child(' + (colIdx+headerAdjust) + '), tr th:nth-child(' + (colIdx+headerAdjust) + ')', tbl).remove();
                else {
                    if(tbl.find("tr td:nth-child(" + (colIdx+headerAdjust) + ") input:text[value!=''], tr th:nth-child(" + (colIdx+headerAdjust) + ") input:text[value!='']").length > 0) {
                        if(confirm('Are you SURE you want to delete this column?\nExisting data will be permanently deleted...'))
                            deleteColumn(tbl, colIdx, true);
                        else
                            return false;
                    } else
                        deleteColumn(tbl, colIdx, true);
                }
            }
            
            function deleteColumns (tbl, colCount)
            {
                var hasHeaderCol = (tbl.find("tbody tr").eq(0).children(0).get(0).tagName == "TH");
                var cols = tbl.find("tbody tr").eq(0).children("td").length;
                //alert("Deleting " + colCount + " columns from " + cols);
                var deleteFromCol = (cols-colCount);
                //alert("Deleting from " + deleteFromCol);
                for(var i=0; i<colCount; i++)
                    deleteColumn(tbl,deleteFromCol,true);
            }

            function insertColumnBefore (tbl, colIdx)
            {
                insertColumn(tbl, colIdx, true);
            }

            function insertColumnAfter (tbl, colIdx)
            {
                insertColumn(tbl, colIdx, false);
            }

            function insertColumn (tbl, colIdx, isBefore)
            {
                // inserts a column before or after colIdx, depending on bool isBefore
                var hasHeaderCol = (tbl.find("tbody tr").eq(0).children(0).get(0).tagName == "TH");
                var headerAdjust = (hasHeaderCol ? 2 : 1);
                var colIdxWidth = $('tr:first td:nth-child(' + (colIdx+headerAdjust) + '), tr:first th:nth-child(' + (colIdx+headerAdjust) + ')', $tbody).children("input").width();
                var $input = $("<input>").attr("type", "text");
                $input.attr("style","width:" + colIdxWidth + "px");
                var $tbody = tbl.find("tbody,tfoot");
                var $td = $("<td>");
                $input.appendTo($td);
                if(isBefore)
                    $('tr td:nth-child(' + (colIdx+headerAdjust) + '), tr th:nth-child(' + (colIdx+headerAdjust) + ')', $tbody).before($td);
                else
                    $('tr td:nth-child(' + (colIdx+headerAdjust) + '), tr th:nth-child(' + (colIdx+headerAdjust) + ')', $tbody).after($td);

                var $thead = tbl.find("thead");
                var $th = $("<th>");
                var $inputh = $("<input>").attr("type", "text");
                $inputh.attr("style","width:" + colIdxWidth + "px");
                $inputh.appendTo($th);
                if(isBefore)
                    $('tr td:nth-child(' + (colIdx+headerAdjust) + '), tr th:nth-child(' + (colIdx+headerAdjust) + ')', $thead).before($th);
                else
                    $('tr td:nth-child(' + (colIdx+headerAdjust) + '), tr th:nth-child(' + (colIdx+headerAdjust) + ')', $thead).after($th);
            }

            function addColumns (tbl, colCount)
            {
                // adds colCount new columns at the end
                var cols = tbl.find("tbody tr").eq(0).children("td").length; 
                for(var i=0; i<colCount; i++)
                    insertColumn(tbl,cols-1,false);
            }


            



            function deleteRow (tbl, rowIdx, doQuietly)
            {
                // deletes row at index row
                if(doQuietly)
                    tbl.find("tbody").children().eq(rowIdx).remove();
                else {
                    if(tbl.find("tbody").children().eq(rowIdx).find("input:text[value!='']").length > 0) {
                        if(confirm('Are you SURE you want to delete this row?\nExisting data will be permanently deleted...'))
                            deleteRow(tbl,rowIdx,true);
                        else
                            return false;
                    } else
                        deleteRow(tbl,rowIdx,true);
                }
            }

            function deleteRows (tbl, rowCount)
            {
                var rows = tbl.find("tbody tr").length;
                tbl.find("tbody tr").slice(rows-rowCount).remove();
                
            }

            function newRow (tbl)
            {
                var $h = tbl.children("thead").eq(0);
                var $b = tbl.children("tbody").eq(0);
                var $f = tbl.children("tfoot").eq(0);
                var hasHeaderCol = ($b[0] && $b.children("tr").children().eq(0).get(0).tagName == "TH");
                var numCols = $("tr", tbl).eq(0).children().length;

                // create array of column widths
                var columnWidths = new Array();
                if($b[0]) {
                    $b.children("tr").eq(0).children().each(function(){
                        columnWidthStyle = $(this).children("input").eq(0).width();
                        columnWidths.push(columnWidthStyle);
                    });
                }

                // DOM fragment for new body row
                var $tnbr = $("<tr>");
                for (var i = 0; i < numCols; i++) {
                    var $tnbrth = (hasHeaderCol && i == 0 ? $("<th>") : $("<td>"));
                    var $input = $("<input>").attr("type", "text");
                    $input.attr("style","width:" + columnWidths[i] + "px");
                    $input.appendTo($tnbrth);
                    $tnbrth.appendTo($tnbr);
                }
                return $tnbr;
            }

            function insertRowBefore (tbl, rowIdx)
            {
                insertRow(tbl, rowIdx, true);
            }

            function insertRowAfter (tbl, rowIdx)
            {
                insertRow(tbl, rowIdx, false);
            }

            function insertRow (tbl, rowIdx, isBefore)
            {
                // inserts a row before or after rowIdx, depending on bool isBefore
                var nr = newRow(tbl);
                if(isBefore)
                    tbl.find("tbody").eq(0).find("tr").eq(rowIdx).before(nr);
                else
                    tbl.find("tbody").eq(0).find("tr").eq(rowIdx).after(nr);
            }

            function addRows (tbl, rowCount)
            {
                // inserts rowCount new rows at the bottom
                var nr = newRow(tbl);
                for(var i=0; i < rowCount; i++)
                    tbl.find("tbody").append(nr.clone());
            }





 
            return this.each(function() {

                var $thisTabularData = $(this);
                var $functions = $(".tabularDataFunctions", $thisTabularData);
                var $resizerLink = $(".showResizer", $functions);
                var $resizer = $(".resizer", $functions);
                var $newSize = $(".newSize", $resizer);
                var $headerCol = $(".tabularDataHeaderColumn input", $functions);
                var $headerRow = $(".tabularDataHeaderRow input", $functions);
                var $footerRow = $(".tabularDataFooterRow input", $functions);


                // get xml from hidden field
                var tableXml = $("input[type=hidden]", $thisTabularData);
                var xmlDoc = parseXML(tableXml.val());
                var $xml = $(xmlDoc);


                // update table size in resizer
                function updateSizer () {
                    $("td",$resizer).removeClass("tableSize");
                    var rows = $tbl.find("tbody tr").length; if(rows > 10) { rows = 10; }
                    var cols = $tbl.find("tbody tr").eq(0).children("td").length; if(cols > 10) { cols = 10; }
                    $resizer.find("tbody tr:lt(" + rows + ")").find("td:lt(" + cols + ")").addClass("tableSize");
                }

                // show resizer
                $(".showResizer",$functions).click(function(){
                    if($functions.hasClass("tableSizeHidden"))
                        $functions.removeClass("tableSizeHidden");
                    else
                        $functions.addClass("tableSizeHidden");
                    this.blur();
                    return false;
                });

                // hide resizer
                $resizer.mouseleave(function(){
                    $functions.addClass("tableSizeHidden");
                });

                // update highlighted cells in resizer
                $functions.delegate("td", "hover", function(event) {

                    var $thisCol = $(this).index();
                    var $thisRow = $(this).parent().index();
                    var $resizer = $(this).parent().parent();

                    if(event.type == 'mouseenter' )
                    {
                        $("td:lt(" + ($thisCol+1) + ") a", $resizer.children("tr").slice(0,$thisRow+1)).addClass("tableResizeTo");
                        $newSize.text(($thisCol+1) + " x " + ($thisRow+1));
                    }
                    else
                    {
                        $("td:lt(" + ($thisCol+1) + ") a", $resizer.children("tr").slice(0,$thisRow+1)).removeClass("tableResizeTo");
                        $newSize.text("");
                    }
                });

                // click resizer
                $functions.delegate("td", "click", function(event) {

                    var $thisCol = $(this).index();
                    var $thisRow = $(this).parent().index();
                    var $resizer = $(this).parent().parent();

                    var rows = $tbl.find("tbody tr").length; 
                    var cols = $tbl.find("tbody tr").eq(0).children("td").length; 
                    var newRows = $thisRow+1;
                    var newCols = $thisCol+1;

                    if(!(newRows==rows && newCols==cols))
                    {
                        // check to see if existing cells containing data will be removed...
                        var doConfirm = false;
                        var doResize = true;
                        if(newRows < rows)
                        {                            
                            if($tbl.find("tbody").children().slice(newRows).find("input:text[value!='']").length > 0) { doConfirm = true; }
                            if(!doConfirm && newCols < cols)
                                if($tbl.find("tbody").children().slice(0,newRows).find("td:gt(" + (newCols-1) + ") input:text[value!=''], th:gt(" + (newCols-1) + ") input:text[value!='']").length > 0) { doConfirm = true; }
                        }
                        else if(newCols < cols)
                            if($tbl.find("tbody").children().find("td:gt(" + (newCols-1) + ") input:text[value!=''], th:gt(" + (newCols-1) + ") input:text[value!='']").length > 0) { doConfirm = true; }


                        if(doConfirm)
                            doResize = confirm("Are you SURE you want to resize the table to " + newCols + " x " + newRows + "?\nExisting data will be permanently deleted...");

                        if(doResize)
                        {               
                            //alert(cols + " x " + rows + " => " + newCols + " x " + newRows + " ... new cols = " + (newCols-cols) + ", new rows = " + (newRows - rows));
                                         
                            if(newRows < rows)
                                deleteRows($tbl,(rows-newRows));
                            else if(newRows > rows)
                                addRows($tbl,(newRows-rows));

                            if(newCols < cols)
                                deleteColumns($tbl,(cols-newCols));
                            else if(newCols > cols)
                                addColumns($tbl,(newCols-cols));

                            tableXml.val(getXmlFromTable($tbl));
                            updateSizer();
                            $functions.addClass("tableSizeHidden");
                        }
                    }
                    return false;
                });


                function changeColumnWidth (colCount)
                {
                    // get existing column width
                    var $b = $tbl.children("tbody").eq(0);

                    var hasHeaderCol = ($tbl.find("tbody tr").eq(0).children(0).get(0).tagName == "TH");
                    var headerAdjust = (hasHeaderCol ? 2 : 1);
                    var colIdxWidth = $('tr:first td:nth-child(' + (colCount+headerAdjust) + '), tr:first th:nth-child(' + (colCount+headerAdjust) + ')', $b).children("input").width();

                    jPrompt('Enter column width (50-500):', colIdxWidth, 'Change column width', function(r) {
                        if(r) {
                    
                            // check to make sure entry is numeric and between 50 and 500px
                            var isOK = false;
                            if(isNumeric(r))
                            {
                                r = r*1;
                                if(r >= 50 && r <= 500) {
                                    isOK = true;
                                    $('tr td:nth-child(' + (colCount+headerAdjust) + '), tr th:nth-child(' + (colCount+headerAdjust) + ')', $tbl).children("input").attr("style","width:" + r + "px");
                                    tableXml.val(getXmlFromTable($tbl));
                                }
                            }
                            if(isOK == false) { jAlert('Column width must be numeric, and between 50 and 500'); }
                        }
                    });
                }


                // update xml when cell content changes
                $(".tabularDataTable", $thisTabularData).delegate("input", "change", function () {
                    tableXml.val(getXmlFromTable($tbl));
                });



                $(".tabularDataTable", $thisTabularData).delegate("input", "focus", function () {
                    $(this).parent().addClass("current");

                    // enable or disable relevant context menu items
                    var hasHeaderCol = $headerCol.attr("checked");
                    var hasHeaderRow = $headerRow.attr("checked");
                    var hasFooterRow = $footerRow.attr("checked");

                    var $m = $(".tabularDataMenu", $thisTabularData);
                    var colIndex = $(this).parent().index();
                    var rowIndex = $(this).parent().parent().index();
                    var section = $(this).parent().parent().parent().get(0).tagName;
                    var sectionElement = "";
                    switch (section) {
                        case "THEAD":
                            $m.disableContextMenuItems('#rowDelete,#rowInsertAbove');
                            $m.enableContextMenuItems('#rowInsertBelow,#colInsertRight');
                            if (colIndex == 0 && hasHeaderCol) { $m.disableContextMenuItems('#colDelete,#colInsertLeft'); } else { $m.enableContextMenuItems('#colDelete,#colInsertLeft'); }
                            break;
                        case "TFOOT":
                            $m.disableContextMenuItems('#rowDelete,#rowInsertBelow');
                            $m.enableContextMenuItems('#rowInsertAbove,#colInsertRight');
                            if (colIndex == 0 && hasHeaderCol) { $m.disableContextMenuItems('#colDelete,#colInsertLeft'); } else { $m.enableContextMenuItems('#colDelete,#colInsertLeft'); }
                            break;
                        default:
                            $m.enableContextMenuItems('#colInsertRight,#rowInsertAbove,#rowInsertBelow,#rowDelete');
                            if (colIndex == 0 && hasHeaderCol) { $m.disableContextMenuItems('#colDelete,#colInsertLeft'); } else { $m.enableContextMenuItems('#colDelete,#colInsertLeft'); }
                            numRows = $(this).parent().parent().siblings().length;
                            if (numRows == 0) { $m.disableContextMenuItems('#rowDelete'); }
                    };
                    numCols = $(this).parent().siblings().length;
                    if ((numCols - (hasHeaderCol ? 1 : 0)) == 0) { $m.disableContextMenuItems('#colDelete'); }
                });

                $(".tabularDataTable", $thisTabularData).delegate("input", "blur", function () {
                    $(this).parent().removeClass("current");
                });


                // handler for changing header col, header row or footer row checkboxes
                $functions.delegate("input", "change", function () {
                    var item = $(this).parent().attr("class");
                    if ($(this).attr("checked")) {

                        var numCols = $("tr", $tbl).eq(0).children().length;
                        var hasHeaderCol = $headerCol.attr("checked");
                        var hasHeaderRow = $headerRow.attr("checked");
                        var hasFooterRow = $footerRow.attr("checked");

                        switch (item) {
                            case "tabularDataHeaderRow":

                                var $thead = $("<thead>");
                                var $tr = $("<tr>");
                                for (var i = 0; i < numCols; i++) {
                                    var $th = (i == 0 && hasHeaderCol) ? $("<th class=\"headerCol\">") : $("<th>");
                                    var $input = $("<input>").attr("type", "text");
                                    $input.appendTo($th);
                                    $th.appendTo($tr);
                                }
                                $tr.appendTo($thead);
                                $tbl.prepend($thead);

                                break;

                            case "tabularDataFooterRow":

                                var $tfoot = $("<tfoot>");
                                var $tr = $("<tr>");
                                for (var i = 0; i < numCols; i++) {
                                    var $th = (hasHeaderCol && i == 0 ? $("<th>") : $("<td>"));
                                    var $input = $("<input>").attr("type", "text");
                                    $input.appendTo($th);
                                    $th.appendTo($tr);
                                }
                                $tr.appendTo($tfoot);
                                $tbl.append($tfoot);

                                break;
                            case "tabularDataHeaderColumn":

                                var $th = $("<th>");
                                var $input = $("<input>").attr("type", "text");
                                $input.appendTo($th);
                                $("tr", $tbl).prepend($th);
                                if(hasHeaderRow){ $tbl.find("thead th").eq(0).addClass("headerCol"); }

                                break;
                        }
                    }
                    else {
                        switch (item) {
                            case "tabularDataHeaderRow":
                                if($tbl.find("thead tr th input:text[value!='']").length > 0) {
                                    if(confirm('Are you SURE you want to remove the header row?\nExisting data in the header row will be permanently deleted...'))
                                        $tbl.find("thead").eq(0).remove();
                                    else {                                    
                                        $(this).attr("checked",true);
                                        return false;
                                    }
                                }
                                else
                                    $tbl.find("thead").eq(0).remove();
                                break;
                            case "tabularDataFooterRow":
                                if($tbl.find("tfoot tr th input:text[value!=''], tfoot tr td input:text[value!='']").length > 0) {
                                    if(confirm('Are you SURE you want to remove the footer row?\nExisting data in the footer row will be permanently deleted...'))
                                        $tbl.find("tfoot").eq(0).remove();
                                    else {                                    
                                        $(this).attr("checked",true);
                                        return false;
                                    }
                                }
                                else
                                    $tbl.find("tfoot").eq(0).remove();
                                break;
                            case "tabularDataHeaderColumn":
                                if($tbl.children().children().find("th:first input:text[value!='']").length > 0) {
                                    if(confirm('Are you SURE you want to remove the header column?\nExisting data in the header column will be permanently deleted...'))
                                        $("tr", $tbl).each(function () {
                                            $(this).children().eq(0).remove();
                                        });
                                    else {
                                        $(this).attr("checked",true);
                                        return false;
                                    }
                                }
                                else
                                    $("tr", $tbl).each(function () {
                                        $(this).children().eq(0).remove();
                                    });
                                break;
                        }
                    }
                    // update XML
                    tableXml.val(getXmlFromTable($tbl));
                });



                // create table from xml
                var $wrap = $("div.tabularDataTable", $thisTabularData);
                $wrap.empty(); 
                
                var $tbl = $("<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\">");

                var hasHeaderRow = false;
                var hasFooterRow = false;
                var hasHeaderCol = false;
                var colCounter = 0;

                // check header column
                hasHeaderCol = $xml.find("Body").eq(0).children(":first").children(":first").get(0).nodeName == "RowHead";

                // get column widths into an array
                var colWidths = new Array();
                $xml.find("Columns").each(function () {
                    $(this).children().each(function() {
                        colWidths.push($(this).attr("width"));
                    });
                });

                 
                // header
                $xml.find("Header").each(function () {
                    hasHeaderRow = true;
                    var $thead = $("<thead>");
                    $(this).children().each(function () {
                        var $tr = $("<tr>");
                        colCounter = 0;
                        $(this).children().each(function () {
                            var $th = (this.nodeName == "ColHead" || this.nodeName == "RowHead" ? $("<th>") : $("<td>"));
                            var $input = $("<input>").attr("type", "text").attr("value", $(this).text());
                            if(colWidths[colCounter]) { $input.attr("style","width:" + colWidths[colCounter++] + "px"); }

                            $input.appendTo($th);
                            $th.appendTo($tr);
                        });                        
                        $tr.appendTo($thead);
                    });
                    $thead.appendTo($tbl);
                });


                // rows
                $xml.find("Body").each(function () {
                    var $tbody = $("<tbody>");
                    $(this).children().each(function () {
                        var $tr = $("<tr>");
                        colCounter = 0;
                        $(this).children().each(function () {
                            //if (this.nodeName == "ColHead" || this.nodeName == "RowHead") { hasHeaderCol = true; }
                            var $th = (this.nodeName == "ColHead" || this.nodeName == "RowHead" ? $("<th>") : $("<td>"));
                            var $input = $("<input>").attr("type", "text").attr("value", $(this).text());
                            if(colWidths[colCounter]) { $input.attr("style","width:" + colWidths[colCounter++] + "px"); }

                            $input.appendTo($th);
                            $th.appendTo($tr);
                        });
                        $tr.appendTo($tbody);
                    });
                    $tbody.appendTo($tbl);
                });


                // footer
                $xml.find("Footer").each(function () {
                    hasFooterRow = true;
                    var $tfoot = $("<tfoot>");
                    $(this).children().each(function () {
                        var $tr = $("<tr>");
                        colCounter = 0;
                        $(this).children().each(function () {
                            var $th = (this.nodeName == "ColHead" || this.nodeName == "RowHead" ? $("<th>") : $("<td>"));
                            var $input = $("<input>").attr("type", "text").attr("value", $(this).text());
                            if(colWidths[colCounter]) { $input.attr("style","width:" + colWidths[colCounter++] + "px"); }

                            $input.appendTo($th);
                            $th.appendTo($tr);
                        });
                        $tr.appendTo($tfoot);
                    });
                    $tfoot.appendTo($tbl);
                });

                if(hasHeaderCol && hasHeaderRow){$tbl.find("thead tr").eq(0).children("th").eq(0).addClass("headerCol");} // <-- add headerCol class to first th of header if hasHeaderCol = true

                $tbl.appendTo($wrap);


                // work out whether we need to tick the header col, header row or footer row checkboxes
                $headerCol.attr('checked', hasHeaderCol);
                $headerRow.attr('checked', hasHeaderRow);
                $footerRow.attr('checked', hasFooterRow);


                $(".tabularDataTable", $thisTabularData).contextMenu({
                    menu: 'tabularDataMenu'
                },
                function (action, el, act, pos) {

                    var hasHeaderCol = $headerCol.attr("checked");
                    var hasHeaderRow = $headerRow.attr("checked");
                    var hasFooterRow = $footerRow.attr("checked");

                    var colIndex = $(act).parent().index() - (hasHeaderCol ? 1 : 0);
                    var rowIndex = $(act).parent().parent().index();
                    var section = $(act).parent().parent().parent().get(0).tagName;

                    var numCols = $("tr", $tbl).eq(0).children().length;


                    // xml fragment for new body row
                    var $tnbr = $("<tr>");
                    for (var i = 0; i < numCols; i++) {
                        var $tnbrth = (hasHeaderCol && i == 0 ? $("<th>") : $("<td>"));
                        var $input = $("<input>").attr("type", "text");
                        $input.appendTo($tnbrth);
                        $tnbrth.appendTo($tnbr);
                    }

                    if(action == 'colWidth') {

                        changeColumnWidth(colIndex);


                    } else {
                        switch (action) {

                            case 'rowDelete':
                                deleteRow($tbl, rowIndex, false);
                                break;

                            case 'rowInsertAbove':
                                if (section == "TFOOT")
                                    addRows($tbl,1);
                                else
                                    insertRow($tbl,rowIndex,true);
                                break;

                            case 'rowInsertBelow':
                                if (section == "THEAD")
                                    insertRow($tbl,0,true);
                                else
                                    insertRow($tbl,rowIndex,false);
                                break;

                            case 'colDelete':
                                deleteColumn($tbl, colIndex, false);
                                break;

                            case 'colInsertLeft':
                                insertColumn($tbl, colIndex, true);
                                break;

                            case 'colInsertRight':
                                insertColumn($tbl, colIndex, false);
                                break;

                        }
                        updateSizer();
                        tableXml.val(getXmlFromTable($tbl));

                    }
                });

              
                updateSizer();
                $functions.addClass("tableSizeHidden");

               
            });
        }
    });
     
})(jQuery);


jQuery(document).ready(function () {
    $(".tabularData").tabularData();
});